package utils.trame;

import utils.BinaryOperation;
import utils.CRC;

/**
 * Represente une {@link Trame} pour le protocole HDLC.
 * 
 * @author Bertholet Damien & Daoust Julien
 */
public class Trame {

	/**
	 * Le polynome generateur utilisé pour le CRC.
	 */
	protected static final String polynomeGenerator = "10001000000100001";

	/**
	 * Les types de {@link Trame}.
	 * 
	 * @author Bertholet Damien & Daoust Julien
	 */
	@SuppressWarnings("javadoc")
	public enum TrameType {
		I, C, A, R, F, P;

		/**
		 * Cherche le {@link TrameType} represente par la string. Renvoie une
		 * exception s'il n'existe pas de {@link TrameType} represente par la
		 * string.
		 * 
		 * @param string
		 *            La string.
		 * @return Le {@link TrameType} represente.
		 */
		private static TrameType find(String string) {
			if(string.equals(I.toString()))
				return I;
			if(string.equals(C.toString()))
				return C;
			if(string.equals(A.toString()))
				return A;
			if(string.equals(R.toString()))
				return R;
			if(string.equals(F.toString()))
				return F;
			if(string.equals(P.toString()))
				return P;

			throw new IllegalArgumentException("Type unknown :" + string);
		}
	}

	private TrameType type;
	private int num;
	private String data;
	private String checksum;

	/**
	 * <p>
	 * Constructeur.
	 * </p>
	 * <p>
	 * Renvoie une exception si la string binaire est mal forme pour la
	 * {@link Trame}.
	 * </p>
	 * 
	 * @param binaryTrame
	 *            Les donnees de la utils.trame sous forme binaire.
	 * @throws MalformedTrameException
	 *             Si la string binaire est mal forme pour la {@link Trame}.
	 */
	public Trame(String binaryTrame) throws MalformedTrameException {
		try {
			String binaryType = binaryTrame.substring(0, 8);
			type =
			        TrameType.find(BinaryOperation
			                .convertBinaryToString(binaryType));

			String binaryNum = binaryTrame.substring(8, 16);
			num = Integer.parseInt(binaryNum, 2);

			int checksumBegin =
			        binaryTrame.length() -
			                (Trame.polynomeGenerator.length() - 1);

			String binaryData = binaryTrame.substring(16, checksumBegin);
			data = BinaryOperation.convertBinaryToString(binaryData);

			checksum = binaryTrame.substring(checksumBegin);
		} catch (Exception e) {
			throw new MalformedTrameException(
			        "Erreur a la construction de la utils.trame", e);
		}
	}

	/**
	 * Constructeur.
	 * 
	 * @param type
	 *            Le type de la {@link Trame}.
	 * @param num
	 *            Le numero de la {@link Trame}.
	 */
	public Trame(TrameType type, int num) {
		this(type, num, null);
	}

	/**
	 * Constructeur.
	 * 
	 * @param type
	 *            Le type de la {@link Trame}.
	 * @param num
	 *            Le numero de la {@link Trame}.
	 * @param data
	 *            Les donnes de la {@link Trame}.
	 */
	public Trame(TrameType type, int num, String data) {
		this.type = type;
		this.num = num;
		this.data = data;
		checksum =
		        CRC.calculCheksum(toStringWihoutCheksum(),
		                Trame.polynomeGenerator);
	}

	/**
	 * Renvoie le type de la {@link Trame}.
	 * 
	 * @return Le type de la {@link Trame}..
	 */
	public TrameType getType() {
		return type;
	}

	/**
	 * Renvoie le numero de la {@link Trame}.
	 * 
	 * @return Le numero de la {@link Trame}.
	 */
	public int getNum() {
		return num;
	}

	/**
	 * Renvoie les donnees de la {@link Trame}.
	 * 
	 * @return Les donnees.
	 */
	public String getData() {
		return data;
	}

	/**
	 * Renvoie le checksum.
	 * 
	 * @return Le checksum.
	 */
	public String getChecksum() {
		return checksum;
	}

	/**
	 * Renvoie si la {@link Trame} est errone. Donc on verifie que la
	 * {@link Trame} n'a pas d'erreur grace au CRC.
	 * 
	 * @return True si la {@link Trame} est correcte. ; False sinon.
	 */
	public boolean isCorrect() {
		return !CRC.hasError(toString(), Trame.polynomeGenerator);
	}

	/**
	 * Renvoie une representation de la utils.trame en binaire ormis le
	 * checksum.
	 * 
	 * @return Une representation de la utils.trame en binaire ormis le
	 *         checksum.
	 */
	private String toStringWihoutCheksum() {
		String binaryType =
		        BinaryOperation.convertStringToBinary(type.toString());

		String binaryNum = Integer.toBinaryString(num);
		binaryNum = BinaryOperation.ajustSize(binaryNum, 8);

		String binaryData = "";
		if(data != null)
			binaryData = BinaryOperation.convertStringToBinary(data);

		return binaryType + binaryNum + binaryData;
	}

	/**
	 * Renvoie une representation de la utils.trame en binaire.
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return toStringWihoutCheksum() + checksum;
	}

	/**
	 * Renvoie une representation de la utils.trame (non binaire).
	 * 
	 * @return Une representation de la utils.trame (non binaire).
	 */
	public String toExpliciteString() {
		return "Type :" + type + "\n" + "Num :" + num + "\n" + "Data :" + data +
		        "\n" + "Checksum :" + checksum;
	}
}